#pragma once
BEGIN_NAMESPACE_OXGE

/************************************************************************/
//
//
/************************************************************************/

template <typename T> 
class MATHLIB_TRANSFER Matrix2T
{
public:
	typedef T ValueType;
	typedef Matrix2T<T> MatrixType;
	typedef Vector2T<T> VectorType;

	Matrix2T(){ }

	Matrix2T(const T* cv){	Set(cv); }
	
	Matrix2T(T m00,T m01,T m10,T m11){ 	
		Set(m00,m01,
			m10,m11);
	}

	inline MatrixType& Set(T am00,T am01,T am10,T am11)
	{
		m00 = am00; m01 = am01; 
		m10 = am10; m11 = am11;
		return *this;
	}

	//set val from a c array
	inline MatrixType& Set(const T* cv)
	{
		memcpy(c_v,cv,sizeof(T) * NRow * NCol);
		return *this;
	}

	//
	T& operator()(size_t r,size_t c)
	{ 
		assert(r<NRow && c<NCol);			
		return c_v[r*NCol+c]; 
	}
	const T& operator()(size_t r,size_t c)const
	{ 
		assert(r<NRow && c<NCol);		
		return c_v[r*NCol+c]; 
	}

	//determination
	T Det()const
	{ 
		return m00*m11 - m01*m10;
	}

	inline operator ValueType*() { return c_v; }	
	inline operator const ValueType*()const { return c_v; }

	//mat * mat
	MatrixType operator * (const MatrixType& matR)const;	

	//mat *= mat
	MatrixType& operator *= (const MatrixType& matR){	return	*this = (*this) * matR;	}

	//Matrix * val
	inline MatrixType operator*(const T& rVal)const{	MatrixType ret = *this;	return ret*=rVal;	}

	//mat *= val
	MatrixType& operator*= (const T& rval)const{
		for(size_t i =0;i<NRow*NCol ;++i)
			c_v[i] *= rVal;
		return *this;
	}

	//mat += mat
	MatrixType& operator+=(const MatrixType& rMat){
		for(size_t i =0;i<NRow*NCol ;++i)
			c_v[i] += rMat.c_v[i];
		return *this;
	}	
	//mat -= mat;
	MatrixType& operator-=(const MatrixType& rMat){
		for(size_t i =0;i<NRow*NCol ;++i)
			c_v[i] -= rMat.c_v[i];
		return *this;
	}

	//Mat - mat;
	MatrixType operator-(const MatrixType rMat)const{
		MatrixType ret = *this;
		return ret -= rMat;
	}

	//mat + mat
	inline MatrixType operator+(const MatrixType& rMat)const{
		MatrixType ret = *this;
		return ret+= rMat;
	}
	//mat ^ p
	inline MatrixType operator^(int p);

	//mat * v
	inline VectorType operator*(const VectorType& vec)const;


	VectorType RowVector(size_t r)const
	{
		return VectorType( (*this)(r,0),(*this)(r,1) );
	}
	VectorType ColVector(size_t c)const
	{
		return VectorType( (*this)(0,c),(*this)(1,c) );
	}
	//
	void SetRow(size_t r, const VectorType& RVec)
	{
		for(size_t i=0;i<NCol;++i)
			(*this)(r,i) = RVec.c_v[i];
	}
	//
	void SetCol(size_t c, const VectorType& CVec)
	{
		for(size_t i=0;i<NRow;++i)
			(*this)(i,c) = CVec.c_v[i];
	}

	//
	inline MatrixType GetTranspose()const{
		MatrixType transMat;
		transpose(transMat, *this);
		return transMat;
	}
	//
	MatrixType& SelfTranspose(){
		transpose(*this, *this);
	}
	//
	inline MatrixType GetInverse()const{
		MatrixType ret;
		inverse(ret, *this);
		return ret;		
	}

	//UnitTest
	static void UnitTest();

public:
	const static size_t NRow = 2;
	const static size_t NCol = 2;
	
	union{
		struct{
			T m00,m01,
			  m10,m11;
		};
		T c_v[NRow*NCol];
	};		

	const static MatrixType IDENTITY;
	const static MatrixType ZERO;

};



//transpose src to dest
template<typename T>
void transpose(Matrix2T<T>& dest, const Matrix2T<T>& src);

//inverse src to dest 
template<typename T> 
bool inverse(Matrix2T<T>& dest, const Matrix2T<T>& src);
//

//global I/O
template<typename T>
std::ostream& operator<<(std::ostream& os, Matrix2T<T> mat)
{
	size_t iRow = 0;	
	for(;iRow < mat.NRow-1;++iRow){
		size_t iCol = 0;
		for(;iCol < mat.NCol-1 ; ++iCol){
			os << mat(iRow, iCol) << "\t";
		}
		os << mat(iRow, iCol);
		os << '\n';
	}
	size_t iCol = 0;
	for(;iCol < mat.NCol-1 ; ++iCol){
		os << mat(iRow, iCol) << "\t";
	}
	os << mat(iRow, iCol);
	os << ';';

	return os;
}

template<typename T>
std::istream& operator>>(std::istream& is, Matrix2T<T>& mat);




END_NAMESPACE_OXGE